"
I am ZnSimplifiedByteEncoder, a concrete subclass of ZnCharacterEncoder.
I handle single byte encodings where byte values 0 to 127 map to ASCII and 128 to 255 are a permutation to Unicode characters.

I am like ZnByteEncoder, a subclass of me, but I implement just two mappings, latin1 or iso-8859-1 and ASCII, to conserve memory.
"
Class {
	#name : 'ZnSimplifiedByteEncoder',
	#superclass : 'ZnCharacterEncoder',
	#instVars : [
		'identifier',
		'byteToUnicode',
		'unicodeToByte',
		'strict'
	],
	#classInstVars : [
		'byteTextConverters'
	],
	#category : 'Zinc-Character-Encoding-Core',
	#package : 'Zinc-Character-Encoding-Core'
}

{ #category : 'mappings' }
ZnSimplifiedByteEncoder class >> asciiMapping [
	"ASCII is only defined for the first 127 codes (7-bit)"

	^ #(
	nil nil nil nil nil nil nil nil
	nil nil nil nil nil nil nil nil
	nil nil nil nil nil nil nil nil
	nil nil nil nil nil nil nil nil
	nil nil nil nil nil nil nil nil
	nil nil nil nil nil nil nil nil
	nil nil nil nil nil nil nil nil
	nil nil nil nil nil nil nil nil
	nil nil nil nil nil nil nil nil
	nil nil nil nil nil nil nil nil
	nil nil nil nil nil nil nil nil
	nil nil nil nil nil nil nil nil
	nil nil nil nil nil nil nil nil
	nil nil nil nil nil nil nil nil
	nil nil nil nil nil nil nil nil
	nil nil nil nil nil nil nil nil )
]

{ #category : 'private' }
ZnSimplifiedByteEncoder class >> byteTextConverters [
	^ byteTextConverters ifNil: [ self initializeByteTextConverters ]
]

{ #category : 'accessing' }
ZnSimplifiedByteEncoder class >> handlesEncoding: string [
	"Return true when my instances handle the encoding described by string"

	^ self byteTextConverters includesKey: (self canonicalEncodingIdentifier: string)
]

{ #category : 'class initialization' }
ZnSimplifiedByteEncoder class >> initialize [
	"Initialize and cache the converters that I know of.
	This includes all of their aliases.
	This method must be changed to make sure it runs
	when loading in images where it is already present."

	self initializeByteTextConverters
]

{ #category : 'private' }
ZnSimplifiedByteEncoder class >> initializeByteTextConverters [
	"Initialize and cache convertors based on specifications in methods that were autogenerated."

	byteTextConverters := Dictionary new.
	self mappingToIdentifiers
		keysAndValuesDo: [ :mapping :identifiers |
			| tables |
			tables := self tablesFromSpec: (self perform: mapping).
			identifiers do: [ :each | byteTextConverters at: each put: tables ] ].
	^ byteTextConverters
]

{ #category : 'mappings' }
ZnSimplifiedByteEncoder class >> iso88591Mapping [
	"Specification generated by my optional subclass, ZnByteEncoder."
	"ZnByteEncoder generateByteToUnicodeSpec: 'http://unicode.org/Public/MAPPINGS/ISO8859/8859-1.TXT'"

	^ #(
	nil nil nil nil nil nil nil nil
	nil nil nil nil nil nil nil nil
	nil nil nil nil nil nil nil nil
	nil nil nil nil nil nil nil nil
	16r00A0 16r00A1 16r00A2 16r00A3 16r00A4 16r00A5 16r00A6 16r00A7
	16r00A8 16r00A9 16r00AA 16r00AB 16r00AC 16r00AD 16r00AE 16r00AF
	16r00B0 16r00B1 16r00B2 16r00B3 16r00B4 16r00B5 16r00B6 16r00B7
	16r00B8 16r00B9 16r00BA 16r00BB 16r00BC 16r00BD 16r00BE 16r00BF
	16r00C0 16r00C1 16r00C2 16r00C3 16r00C4 16r00C5 16r00C6 16r00C7
	16r00C8 16r00C9 16r00CA 16r00CB 16r00CC 16r00CD 16r00CE 16r00CF
	16r00D0 16r00D1 16r00D2 16r00D3 16r00D4 16r00D5 16r00D6 16r00D7
	16r00D8 16r00D9 16r00DA 16r00DB 16r00DC 16r00DD 16r00DE 16r00DF
	16r00E0 16r00E1 16r00E2 16r00E3 16r00E4 16r00E5 16r00E6 16r00E7
	16r00E8 16r00E9 16r00EA 16r00EB 16r00EC 16r00ED 16r00EE 16r00EF
	16r00F0 16r00F1 16r00F2 16r00F3 16r00F4 16r00F5 16r00F6 16r00F7
	16r00F8 16r00F9 16r00FA 16r00FB 16r00FC 16r00FD 16r00FE 16r00FF )
]

{ #category : 'accessing' }
ZnSimplifiedByteEncoder class >> knownEncodingIdentifiers [
	^ self byteTextConverters keys
]

{ #category : 'mappings' }
ZnSimplifiedByteEncoder class >> mappingToIdentifiers [
	"Return a dictionay mapping from encoding specifications to a list of encoding names."

	^ Dictionary newFromPairs: #(
		#iso88591Mapping #('iso88591' 'latin1')
		#asciiMapping #('ascii') )
]

{ #category : 'instance creation' }
ZnSimplifiedByteEncoder class >> newForEncoding: string [
	"Return a new character encoder object for an encoding described by string.
	We use our precomputed ByteTextConverters tables."

	| tables canonicalName |
	canonicalName := self canonicalEncodingIdentifier: string.
	tables := self byteTextConverters at: canonicalName.
	^ self new
		identifier: canonicalName;
		byteToUnicode: tables first;
		unicodeToByte: tables second;
		yourself
]

{ #category : 'private' }
ZnSimplifiedByteEncoder class >> tablesFromSpec: mapping [
	"Initialize the mappings to and from Unicode based on the 128 element array mapping"

	| byteToUnicode unicodeToByte |
	byteToUnicode := Array new: 128.
	unicodeToByte := Dictionary new.
	"Mind the offset because first 128 characters are not stored into byteToUnicodeSpec"
	"Note that some entries are nil"
	mapping
		keysAndValuesDo: [ :index :unicode |
			unicode ifNotNil: [
				byteToUnicode at: index put: (Character value: unicode).
				unicodeToByte at: unicode put: 127 + index ] ].
	^ Array with: byteToUnicode with: unicodeToByte
]

{ #category : 'comparing' }
ZnSimplifiedByteEncoder >> = anObject [
	^ super = anObject and: [ self identifier = anObject identifier ]
]

{ #category : 'encoding - decoding' }
ZnSimplifiedByteEncoder >> backOnStream: stream [
	stream back
]

{ #category : 'initialization' }
ZnSimplifiedByteEncoder >> beLenient [
	"Don't be strict, which is the default.
	This means that holes in the mapping are let to pass through."

	strict := false
]

{ #category : 'accessing' }
ZnSimplifiedByteEncoder >> byteDomain [
	"Return an array with the byte values that are in my domain, that I can decode"

	^ (0 to: 127) asArray , ((byteToUnicode withIndexCollect: [ :each :index | each ifNotNil: [ index + 127 ] ]) reject: [ :each | each isNil ])
]

{ #category : 'initialization' }
ZnSimplifiedByteEncoder >> byteToUnicode: map [
	byteToUnicode := map
]

{ #category : 'accessing' }
ZnSimplifiedByteEncoder >> characterDomain [
	"Return a set with the characters that are in my domain, that I can encode"

	^ ((0 to: 127) asSet, unicodeToByte keys) collect: [ :each | Character value: each  ]
]

{ #category : 'encoding - decoding' }
ZnSimplifiedByteEncoder >> encodedByteCountFor: character [
	"Overwritten for performance reasons"

	^ 1
]

{ #category : 'encoding - decoding' }
ZnSimplifiedByteEncoder >> encodedByteCountForCodePoint: character [
	^ 1
]

{ #category : 'encoding - decoding' }
ZnSimplifiedByteEncoder >> ensureAtBeginOfCodePointOnStream: stream [
	"Ensure that the current position of stream is a the beginning of an encoded code point,
	if not move further backwards. This is necessary when a position in the binary stream is set,
	not knowing if that position is on a proper encoded character boundary."

	"Nothing to be done, I am a byte encoding: each code point is encoded in a single byte"
]

{ #category : 'comparing' }
ZnSimplifiedByteEncoder >> hash [
	^ self identifier hash
]

{ #category : 'accessing' }
ZnSimplifiedByteEncoder >> identifier [
	^ identifier
]

{ #category : 'initialization' }
ZnSimplifiedByteEncoder >> identifier: object [
	identifier := object
]

{ #category : 'initialization' }
ZnSimplifiedByteEncoder >> initialize [
	super initialize.
	strict := true
]

{ #category : 'testing' }
ZnSimplifiedByteEncoder >> isLenient [
	^ strict not
]

{ #category : 'testing' }
ZnSimplifiedByteEncoder >> isStrict [
	^ strict
]

{ #category : 'encoding - decoding' }
ZnSimplifiedByteEncoder >> nextCodePointFromStream: stream [
	"In non-strict mode, we let byte values for holes in our mapping pass through"

	| byteValue |
	^ (byteValue := stream next) < 128
		ifTrue: [ byteValue ]
		ifFalse: [
			(byteToUnicode at: byteValue - 127 ifAbsent: [ nil ])
				ifNotNil: [ :unicode | unicode asInteger ]
				ifNil: [
					strict
						ifTrue: [ self errorOutsideRange ]
						ifFalse: [ byteValue ] ] ]
]

{ #category : 'encoding - decoding' }
ZnSimplifiedByteEncoder >> nextFromStream: stream [
	"In non-strict mode, we let byte values for holes in our mapping pass through.
	Overwritten for performance reasons"

	| byteValue |
	^ (byteValue := stream next ifNil: [ ^ self errorIncomplete asCharacter ]) < 128
		ifTrue: [ Character value: byteValue ]
		ifFalse: [
			(byteToUnicode at: byteValue - 127 ifAbsent: [ nil ])
				ifNotNil: [ :unicode | unicode ]
				ifNil: [
					strict
						ifTrue: [ self errorOutsideRange ]
						ifFalse: [ Character value: byteValue ] ] ]
]

{ #category : 'encoding - decoding' }
ZnSimplifiedByteEncoder >> nextPutCodePoint: codePoint toStream: stream [
	"In non-strict mode, we let code points for holes in our mapping table pass through"

	codePoint < 128
		ifTrue: [ stream nextPut: codePoint ]
		ifFalse: [
			| byte |
			byte := unicodeToByte at: codePoint ifAbsent: [ nil ].
			(byte isNil and: [ strict or: [ codePoint > 255 ] ])
				ifTrue: [ self errorOutsideRange ].
			stream nextPut: (byte ifNil: [ codePoint ]) ]
]

{ #category : 'printing' }
ZnSimplifiedByteEncoder >> printOn: stream [
	super printOn: stream.
	stream
		nextPut: $(;
		print: identifier.
	strict
		ifTrue: [ stream nextPutAll: ' strict' ].
	stream nextPut: $)
]

{ #category : 'encoding - decoding' }
ZnSimplifiedByteEncoder >> skipToBeginOfCodePointOnStream: stream [
	"Ensure that the current position of stream is a the beginning of an encoded code point,
	if not move further forward. This is necessary when a position in the binary stream is set,
	not knowing if that position is on a proper encoded character boundary."

	"Nothing to be done, I am a byte encoding: each code point is encoded in a single byte"
]

{ #category : 'initialization' }
ZnSimplifiedByteEncoder >> unicodeToByte: map [
	unicodeToByte := map
]
